﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

using RexToy;
using RexToy.Xml;

namespace ScriptXCompiler
{
    public class CodeTemplate
    {
        private string _name;
        public string Name
        {
            get { return _name; }
        }

        public string NamespaceTemplate { get; set; }

        private string _classBeginTemplate;
        public string ClassBeginTemplate
        {
            get { return _classBeginTemplate; }
        }

        private string _classEndTemplate;
        public string ClassEndTemplate
        {
            get { return _classEndTemplate; }
        }

        private bool _standaloneCtor;
        public bool StandaloneCtor
        {
            get { return _standaloneCtor; }
        }

        private string _ctorBeginTemplate;
        public string CtorBeginTemplate
        {
            get { return _ctorBeginTemplate; }
        }

        private string _ctorEndTemplate;
        public string CtorEndTemplate
        {
            get { return _ctorEndTemplate; }
        }

        public bool SupportInterface { get; private set; }
        private string _interfaceBeginTemplate;
        public string InterfaceBeginTemplate
        {
            get { return _interfaceBeginTemplate; }
        }

        private string _interfaceEndTemplate;
        public string InterfaceEndTemplate
        {
            get { return _interfaceEndTemplate; }
        }

        public bool SupportEnum { get; private set; }

        private string _enumBeginTemplate;
        public string EnumBeginTemplate
        {
            get { return _enumBeginTemplate; }
        }

        private string _enumEndTemplate;
        public string EnumEndTemplate
        {
            get { return _enumEndTemplate; }
        }

        private CodeTemplate(string text)
        {
            XDoc x = XDoc.LoadFromString(text);
            var xName = x.NavigateToSingle("name");
            this._name = xName.GetStringValue();

            var xNamespace = x.NavigateToSingle("namespace");
            var xClass = x.NavigateToSingle("class");
            var xCtor = x.NavigateToSingleOrNull("constructor");
            var xInterface = x.NavigateToSingleOrNull("interface");
            var xEnum = x.NavigateToSingleOrNull("enum");

            this.NamespaceTemplate = xNamespace.GetStringValue().Trim();

            SplitTemplate(xClass.GetStringValue(), out this._classBeginTemplate, out this._classEndTemplate);

            if (xInterface == null || string.IsNullOrEmpty(xInterface.GetStringValue().Trim()))
                this.SupportInterface = false;
            else
            {
                SplitTemplate(xInterface.GetStringValue(), out this._interfaceBeginTemplate, out this._interfaceEndTemplate);
                this.SupportInterface = true;
            }

            if (xEnum == null || string.IsNullOrEmpty(xEnum.GetStringValue().Trim()))
                this.SupportEnum = false;
            else
            {
                SplitTemplate(xEnum.GetStringValue(), out this._enumBeginTemplate, out this._enumEndTemplate);
                this.SupportEnum = true;
            }

            if (xCtor == null || string.IsNullOrEmpty(xCtor.GetStringValue().Trim()))
                this._standaloneCtor = false;
            else
            {
                SplitTemplate(xCtor.GetStringValue(), out this._ctorBeginTemplate, out this._ctorEndTemplate);
                this._standaloneCtor = true;
            }
        }

        private static void SplitTemplate(string template, out string begin, out string end)
        {
            int codeLineIdx = -1;

            var lines = template.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
            for (int i = 0; i < lines.Length; i++)
            {
                var line = lines[i];
                if (line.Trim() == "%code%")
                    codeLineIdx = i;
            }

            Assertion.IsTrue(codeLineIdx != -1, "Code class template not correct, line %code% not found.");

            begin = string.Empty;
            for (int i = 0; i < codeLineIdx; i++)
                begin += lines[i] + Environment.NewLine;

            end = string.Empty;
            for (int i = codeLineIdx + 1; i < lines.Length; i++)
                end += lines[i] + Environment.NewLine;
        }

        public static CodeTemplate Parse(string path)
        {
            var txt = File.ReadAllText(path);
            return new CodeTemplate(txt);
        }

        public static CodeTemplate Parse(StreamReader stream)
        {
            var txt = stream.ReadToEnd();
            return new CodeTemplate(txt);
        }
    }
}
